package com.zjpavt.client.udp;

import com.zjpavt.client.hal.KeyPad;
import com.zjpavt.client.udp.udpservice.HangzhouMeasureParameter;
import com.zjpavt.client.udp.udpservice.UdpService;
import com.zjpavt.client.util.ClientConfig;
import com.zjpavt.client.util.LoggerUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * @Description: 用于接收网络udp信息
 * @author: zyc
 */
public class UdpServer {

    private static final Logger log = LoggerFactory.getLogger(UdpServer.class);

    private static class InnerClass {
        static final UdpServer udpServer = new UdpServer();
    }

    public static UdpServer getInstance() {
        return InnerClass.udpServer;
    }

    private UdpServer() {
//		initServer(ClientConfig.OUTPUT_UDP_SERVER_PORT);
//		initServer(20192);
    }

    private ChannelHandlerContext udpClient;

    private UdpService udpService = new HangzhouMeasureParameter();

    private int port;

    public ChannelHandlerContext getUdpClient() {
        return udpClient;
    }

    public void setUdpClient(ChannelHandlerContext udpClient) {
        this.udpClient = udpClient;
    }

    private void initServer(int port) {
        final NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.channel(NioDatagramChannel.class);
        bootstrap.group(nioEventLoopGroup);
        bootstrap.handler(new ChannelInitializer<NioDatagramChannel>() {
            @Override
            protected void initChannel(NioDatagramChannel ch) throws Exception {
                ch.pipeline().addLast(new UdpServerHandler());
            }
        });
        try {
            log.error("begin start UdpServer success!!!------------------------");
            ChannelFuture bind = bootstrap.bind(port).sync();
            this.port = port;
            log.info("start UdpServer success!");
            bind.channel().closeFuture().await();
        } catch (Exception e) {
            log.error(LoggerUtil.convertException2String(e));
        }
        finally {
            nioEventLoopGroup.shutdownGracefully();
        }
    }

    private class UdpServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
            ByteBuf buf = msg.copy().content();
            byte[] req = new byte[buf.readableBytes()];
            buf.readBytes(req);
            ReferenceCountUtil.release(buf);
            String res = new String(req, ClientConfig.DEFAULT_ENCODING);
            log.trace("received from " + msg.sender().getHostString() +":"+ res);
            //过滤掉非本机发送的udp信息，增加安全性
            String host = msg.sender().getHostString();
            if (ClientConfig.OUTPUT_UDP_SERVER_IP.equals(host)) {
                dispatchMsg(res);
            } else {
                dispatchMsgOtherHost(res,ctx);
            }
        }
    }

    private final static Pattern KEYPAD_PATTERN = Pattern.compile("^KeyPressed value:(0x[0-9,a-f]{1,2})$");
    private final static Pattern DOOR_PATTERN = Pattern.compile("^<DOOR_([a-z])>\\n$");
    private void dispatchMsg(String msg) {
//		log.info("udp server receive a message at port 6464 :" + msg);
        Matcher matcher = KEYPAD_PATTERN.matcher(msg.trim());
        if (matcher.matches()) {
            KeyPad.getInstance().notifyChanged(matcher.group(1));
        }
    }


    private void dispatchMsgOtherHost(String msg, ChannelHandlerContext channelHandlerContext) {
        log.info("udp server receive a message at port " + port + " :" + msg);
        udpService.execute(msg, channelHandlerContext);
    }

    public interface UdpDispatch {
        public void notifyChanged(String udpMsg);
    }

}
